# -*- coding: utf-8 -*-
"""excel2pdf_mixin
将用户原版 Excel (WPS) 批量转 PDF 逻辑封装为 mixin，
以融入主程序的 CustomTkinter 界面与线程体系。
"""

from __future__ import annotations

import os
import threading
import time
from datetime import datetime
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox

import win32com.client
import subprocess

__all__ = ["Excel2PdfMixin"]


class Excel2PdfMixin:  # noqa: D101
    # ------------------------------------------------------------------
    # 文件选择 & UI 更新
    # ------------------------------------------------------------------
    def select_excel_files(self):  # type: ignore[override]
        if self.is_converting:
            messagebox.showwarning("警告", "正在进行其他转换，请稍后")
            return
        files = filedialog.askopenfilenames(title="选择Excel文件", filetypes=[("Excel files", "*.xlsx *.xls")])
        if files:
            self.excel_paths = list(files)
            self.update_excel_file_list()

    def select_excel_output_folder(self):  # type: ignore[override]
        if self.is_converting:
            messagebox.showwarning("警告", "正在进行其他转换，请稍后")
            return
        folder = filedialog.askdirectory(title="选择PDF输出目录")
        if folder:
            self.excel_output_dir = folder
            if hasattr(self, "excel_output_entry"):
                self.excel_output_entry.delete(0, tk.END)
                self.excel_output_entry.insert(0, folder)
            self.update_excel_file_list()

    def update_excel_file_list(self):  # type: ignore[override]
        if not hasattr(self, "excel_file_listbox"):
            return
        self.excel_file_listbox.configure(state="normal")
        self.excel_file_listbox.delete("1.0", "end")
        self.excel_file_listbox.insert("1.0", "选择的Excel文件：\n\n")
        for p in self.excel_paths:
            self.excel_file_listbox.insert("end", f"{os.path.basename(p)}\n")
        if getattr(self, "excel_output_dir", ""):
            self.excel_file_listbox.insert("end", f"\n输出目录：{self.excel_output_dir}\n")
        self.excel_file_listbox.configure(state="disabled")

    # ------------------------------------------------------------------
    # 启动转换
    # ------------------------------------------------------------------
    def start_excel2pdf_conversion(self):  # type: ignore[override]
        if self.is_converting:
            messagebox.showwarning("提示", "正在进行其他转换，请稍后")
            return
        if not getattr(self, "excel_paths", []):
            messagebox.showerror("错误", "请先选择Excel文件")
            return
        if not getattr(self, "excel_output_dir", ""):
            messagebox.showerror("错误", "请先选择输出目录")
            return
        # UI 状态
        self.is_converting = True
        self.excel_convert_btn.configure(state="disabled")
        self.excel_status_label.configure(text="开始转换...")
        self.excel_progress_bar.set(0)
        threading.Thread(target=self.convert_excel_thread, daemon=True).start()

    # ------------------------------------------------------------------
    # 核心线程逻辑
    # ------------------------------------------------------------------
    def convert_excel_thread(self):  # type: ignore[override]
        success_count = 0
        error_files: list[tuple[str, str]] = []
        total_files = len(self.excel_paths)
        # 初始化 Excel 内核：优先 WPS(KET)，失败则尝试 Microsoft Excel
        excel = self._init_excel_app()
        if excel is None:
            # 两种内核都无法启动
            self.after(0, self._reset_excel_ui_state)
            return
        try:
            for idx, file_path in enumerate(self.excel_paths, 1):
                if not self.is_converting:
                    break
                status = f"正在转换 {idx}/{total_files}: {Path(file_path).name}"
                self.after(0, self._update_excel_progress_ui, idx / total_files, status)
                try:
                    wb = excel.Workbooks.Open(file_path)
                    for ws in wb.Worksheets:
                        ws.PageSetup.Orientation = 2 if self.excel_orientation.get() == "landscape" else 1
                        ws.PageSetup.CenterHorizontally = self.excel_center_h.get()
                        ws.PageSetup.CenterVertically = self.excel_center_v.get()
                        if self.excel_fit_page.get():
                            ws.PageSetup.FitToPagesWide = 1
                            ws.PageSetup.FitToPagesTall = False
                        else:
                            ws.PageSetup.Zoom = self.excel_scale.get()
                        ws.Columns.AutoFit()
                    pdf_name = Path(file_path).with_suffix('.pdf').name
                    pdf_path = os.path.join(self.excel_output_dir, pdf_name)
                    wb.ExportAsFixedFormat(0, pdf_path)
                    time.sleep(0.5)
                    wb.Close(SaveChanges=False)
                    time.sleep(0.4)
                    success_count += 1
                except Exception as exc:  # noqa: BLE001
                    error_files.append((file_path, str(exc)))
        finally:
            try:
                excel.Quit()
            except Exception:
                pass
            self.cleanup_excel_processes()
            self.after(0, self.create_excel_report, success_count, error_files, total_files)
            self.after(0, self._reset_excel_ui_state)

    # ------------------------------------------------------------------
    # 辅助方法
    # ------------------------------------------------------------------
    def _update_excel_progress_ui(self, progress: float, status: str):
        self.excel_progress_bar.set(progress)
        self.excel_status_label.configure(text=status)
        self.update_idletasks()

    def _reset_excel_ui_state(self):
        self.is_converting = False
        self.excel_convert_btn.configure(state="normal")
        self.excel_status_label.configure(text="准备就绪")
        self.excel_progress_bar.set(0)

    def create_excel_report(self, success_count: int, error_files: list[tuple[str, str]], total: int):
        if success_count:
            messagebox.showinfo("完成", f"转换完成！\n成功: {success_count}\n失败: {len(error_files)}")
        if error_files:
            log_path = os.path.join(os.path.dirname(self.excel_paths[0]), "Excel转换错误日志.txt")
            if self.excel_output_dir:
                log_path = os.path.join(self.excel_output_dir, "Excel转换错误日志.txt")
            try:
                with open(log_path, "w", encoding="utf-8") as f:
                    f.write(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
                    for fp, err in error_files:
                        f.write(f"文件: {Path(fp).name}\n错误: {err}\n\n")
                messagebox.showwarning("部分失败", f"部分文件转换失败，详情见:\n{log_path}")
            except Exception as exc:  # noqa: BLE001
                messagebox.showerror("错误", f"写入日志失败: {exc}")

    def cleanup_excel_processes(self):
        try:
            proc = getattr(self, "_excel_process_name", "")
            if proc:
                subprocess.run(
                    f"taskkill /f /im {proc}",
                    shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    check=False,
                )
        except Exception:
            pass

    # ---------------------- 内核检测 ----------------------
    def _init_excel_app(self):
        """尝试创建 WPS (KET) 或 Microsoft Excel COM 对象。返回对象或 None。"""
        app = None
        # 1. 尝试 WPS
        try:
            app = win32com.client.Dispatch("KET.Application")  # noqa: S110
            self._excel_process_name = "et.exe"
        except Exception:
            pass

        if app is None:
            # 2. 尝试 Microsoft Excel
            try:
                app = win32com.client.Dispatch("Excel.Application")  # noqa: S110
                self._excel_process_name = "EXCEL.EXE"
            except Exception:
                messagebox.showerror("错误", "无法启动 WPS 或 Microsoft Excel，无法进行转换。")
                return None

        # 通用设置
        try:
            app.Visible = False
            app.DisplayAlerts = False
        except Exception:
            pass

        return app